#pragma once
#include <iostream>
#include <cerrno>
#include <functional>
#include <unordered_map>
#include <string>
#include "Socket.hpp"
#include "Epoll.hpp"
#include "Log.hpp"
#include "Protocol.hpp"

class Connection;
class ReactorServer;

using func_t = std::function<void(Connection *)>;
// 回调函数，处理完数据交给上层
using callback_t = std::function<void(Connection *, std::string &)>;
class Connection
{
public:
    Connection(int sock = -1) : _fd(sock), _server(nullptr)
    {
    }
    void SetFunc(func_t recv_cb, func_t send_cb, func_t except_cb)
    {

        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
        // logMessage(DEBUG, "recv_cb:%p,ServerAccept:%p", &(_recv_cb), &recv_cb);
    }

public:
    int _fd;
    func_t _recv_cb = nullptr;
    func_t _send_cb = nullptr;
    func_t _except_cb = nullptr;
    //
    ReactorServer *_server;

    // 缓冲区
    std::string _inBuffer;
    std::string _outBuffer;
};
class ReactorServer
{
    static const int DEFAULT_PORT = 8080;
    static const int DEFAULT_TIMEOUT = 0;
    static const int gnum = 128;

public:
    ReactorServer(int server_port = DEFAULT_PORT) : _server_port(server_port), _eventNum(gnum), _timeout(DEFAULT_TIMEOUT)
    {
        _events = new epoll_event[_eventNum];
        // 引入网络流
        _listen_fd = Socket::Sock();
        Socket::Bind(_listen_fd, _server_port);
        Socket::Listen(_listen_fd);
        // 创建epoll模型
        _poll.Epoll::CreateEpoll();
        // 添加listen sock到epoll模型中
        AddConnToServer(_listen_fd, std::bind(&ReactorServer::ServerAccept, this, std::placeholders::_1), nullptr, nullptr);
    }
    void Dispatcher(callback_t cb)
    {
        _cb = cb;
        while (true)
        {
            LoopOnce();
            sleep(1);
        }
    }
    void SetReadWrite(int sock, bool read, bool write)
    {
        uint32_t event = ((read ? EPOLLIN : 0) | (write ? EPOLLOUT : 0));
        if (!_poll.CtrlEpoll(sock, event))
        {
            assert(false); // or 把sock关系给直接关闭
            // logMessage(ERROR,"setreadwrite error");
        }
    }

private:
    void AddConnToServer(int fd, func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        // 设置非阻塞
        Socket::SetNoBlock(fd);
        Connection *con = new Connection(fd);
        con->_server = this;
        con->SetFunc(recv_cb, send_cb, except_cb);
        // logMessage(DEBUG, "recv_cb:%p,ServerAccept:%p", &(con->_recv_cb), &ReactorServer::ServerAccept);
        // connection到fd的映射关系填上
        _fdConnMap[fd] = con;
        // 设置ET模式
        if (!_poll.AddSockToEpoll(fd, EPOLLIN | EPOLLET))
        {
            exit(5);
        }
        logMessage(DEBUG, "fd set success fd[%d]", fd);
    }

    void LoopOnce()
    {
        int num = _poll.WaitEpoll(_events, _eventNum, _timeout);
        switch (num)
        {
        case -1:
            logMessage(ERROR, "epoll wait error,errno:%d,%s\n", errno, strerror(errno));
            break;
        case 0:
            // logMessage(DEBUG, "epoll wait超时");
            break;
        default:
            logMessage(DEBUG, "epoll wait success,有 %d 个事件", num);
            HanderEvent(num);
            break;
        }
    }
    void HanderEvent(int num)
    {
        for (int i = 0; i < num; i++)
        {
            int sock = _events[i].data.fd;
            // auto it = _fdConnMap.find(sock);
            // if (it == _fdConnMap.end())
            // {
            //     logMessage(ERROR, "HanderEvent error,fd:%d,errno:%d,%s\n", _events[i].data.fd, errno, strerror(errno));
            //     return;
            // }
            Connection *con = _fdConnMap[sock];
            uint32_t events = _events[i].events;
            if(events & EPOLLERR)   _events[i].events |= (EPOLLIN | EPOLLOUT);
            if(events & EPOLLHUP)   _events[i].events |= (EPOLLIN | EPOLLOUT);

            if (_events[i].events & EPOLLIN)
            {
                if (con->_recv_cb != nullptr)
                {
                    logMessage(DEBUG, "fd[%d]read事件", con->_fd);
                    con->_recv_cb(con);
                }
            }
            if (_events[i].events & EPOLLOUT)
            {
                if (con->_send_cb != nullptr)
                {
                    logMessage(DEBUG, "fd[%d]write事件", con->_fd);
                    con->_send_cb(con);
                }
            }
        }
    }
    void ServerAccept(Connection *con)
    {
        while (true)
        {
            std::string ip;
            uint16_t port;
            int accept_errno;
            int sock = Socket::Accept(con->_fd, &ip, &port, &accept_errno);
            if (sock < 0)
            {
                if (accept_errno == EAGAIN || accept_errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (accept_errno == EINTR)
                {
                    continue;
                }
                else
                {
                    logMessage(ERROR, "accept errno,fd[%d],errno[%d]:,%s", sock, errno, strerror(errno));
                }
            }
            else
            {
                logMessage(DEBUG, "accept fd[%d],client ip:%s,%d:\n", sock, ip.c_str(), port);
                // 加入epoll中
                AddConnToServer(sock, std::bind(&ReactorServer::Receive, this, std::placeholders::_1),
                                std::bind(&ReactorServer::SendClient, this, std::placeholders::_1),
                                std::bind(&ReactorServer::Execpt, this, std::placeholders::_1));
            }
        }
    }
    void Receive(Connection *con)
    {
        int sock = con->_fd;
        char buffer[1024];
        ssize_t num = 1;
        while (true)
        {
            num = recv(sock, buffer, sizeof(buffer) - 1, 0);
            if (num < 0)
            {
                if (errno == EAGAIN | errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    logMessage(ERROR, "recv errno fd[%d]", con->_fd);
                    con->_except_cb(con);
                    // _poll.DelSockToEpoll(con->_fd, EPOLLIN);
                    // close(con->_fd);
                    // delete con;
                    // return;
                }
            }
            else if (num == 0)
            {
                logMessage(DEBUG, "client close fd[%d]", con->_fd);
                con->_except_cb(con);
                // _poll.DelSockToEpoll(con->_fd, EPOLLIN);
                // close(con->_fd);
                // delete con;
                // return;
            }
            else
            {
                buffer[num] = 0;
                con->_inBuffer += buffer;
            }
        }
        logMessage(DEBUG, "client:%s", con->_inBuffer.c_str());
        std::vector<std::string> outs;
        SpliteMessage(con->_inBuffer, outs);
        // 交给上层进行处理
        for (auto str : outs)
        {
            // 可以把str封装成任务交给线程池
            // std::cout << str << std::endl;
            _cb(con, str);
        }
    }
    void SendClient(Connection *con)
    {
        while (true)
        {
            ssize_t num = send(con->_fd, con->_outBuffer.c_str(), con->_outBuffer.size(), 0);
            if (num > 0)
            {
                con->_outBuffer.erase(0, num);
                if (con->_outBuffer.empty())
                    break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    logMessage(ERROR, "send fd[%d] error", con->_fd);
                    con->_except_cb;
                    break;
                }
            }
        }
        if (con->_outBuffer.empty())
            SetReadWrite(con->_fd, true, false);
        else
            SetReadWrite(con->_fd, true, true);
    }
    void Execpt(Connection *con)
    {
        if (!IsSockInMap(con->_fd))
            return;
        int sock = con->_fd;
        _poll.DelSockToEpoll(con->_fd, EPOLLIN);
        close(con->_fd);
        delete con;
        logMessage(DEBUG, "fd[%d] close,errno[%d]:%s", sock, errno, strerror(errno));
    }
    bool IsSockInMap(int sock)
    {
        auto it = _fdConnMap.find(sock);
        if (it == _fdConnMap.end())
        {
            return false;
        }
        else
        {
            return true;
        }
    }

private:
    int _listen_fd;
    int _server_port;
    std::unordered_map<int, Connection *> _fdConnMap;
    epoll_event *_events;
    int _eventNum;
    int _timeout;
    Epoll _poll;
    // 回调函数
    callback_t _cb;
};